package com.googlecode.dgwt.client.dijit.base;

import java.util.Iterator;
import java.util.List;

import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.WidgetCollection;
import com.googlecode.dgwt.client.dijit.Dijit;
import com.googlecode.dgwt.client.dijit.HTML;
import com.googlecode.dgwt.client.dijit.Widget;
import com.googlecode.dgwt.client.dijit.base.widget.WidgetEngine;
import com.googlecode.dgwt.client.dijit.theme.CSS;
import com.googlecode.dgwt.client.dojo.Dojo;

/**
 * Abstract base class for all DGWT panels, which are widgets that can contain other
 * widgets.
 * 
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class PanelWidget extends Widget implements HasWidgets {
        
    protected PanelWidget(boolean ready) {
        super(ready);
    }
    
    protected PanelWidget(Element element) {
        super(element);
    }

    /* (non-Javadoc)
     * @see com.googlecode.dgwt.client.dijit.Widget#getThemeCSS()
     */
    @Override
    public CSS getThemeCSS() {
        return Dijit.getTheme().dijitPanelWidget();
    }

    /**
     * Adds a child widget.
     * 
     * <p>
     * <b>How to Override this Method</b>
     * </p>
     * <p>
     * There are several important things that must take place in the correct
     * order to properly add or insert a Widget to a Panel. Not all of these
     * steps will be relevant to every Panel, but all of the steps must be
     * considered.
     * <ol>
     * <li><b>Validate:</b> Perform any sanity checks to ensure the Panel can
     * accept a new Widget. Examples: checking for a valid index on insertion;
     * checking that the Panel is not full if there is a max capacity.</li>
     * <li><b>Adjust for Reinsertion:</b> Some Panels need to handle the case
     * where the Widget is already a child of this Panel. Example: when
     * performing a reinsert, the index might need to be adjusted to account for
     * the Widget's removal. See {@link ComplexPanel#adjustIndex(Widget, int)}.</li>
     * <li><b>Detach Child:</b> Remove the Widget from its existing parent, if
     * any. Most Panels will simply call {@link Widget#removeFromParent()} on
     * the Widget.</li>
     * <li><b>Logical Attach:</b> Any state variables of the Panel should be
     * updated to reflect the addition of the new Widget. Example: the Widget is
     * added to the Panel's {@link WidgetCollection} at the appropriate index.</li>
     * <li><b>Physical Attach:</b> The Widget's Element must be physically
     * attached to the Panel's Element, either directly or indirectly.</li>
     * <li><b>Adopt:</b> Call {@link #adopt(Widget)} to finalize the add as the
     * very last step.</li>
     * </ol>
     * </p>
     * 
     * @param child
     *            the widget to be added
     * @throws UnsupportedOperationException
     *             if this method is not supported (most often this means that a
     *             specific overload must be called)
     * @see HasWidgets#add(Widget)
     */
    public void add(com.google.gwt.user.client.ui.Widget child) {
        throw new UnsupportedOperationException("This panel does not support no-arg add()");
    }

    /**
     * Adds a new widget from a HTML fragment. <br/>
     * It's just a short way to <code>
     * {@link #add(com.google.gwt.user.client.ui.Widget) add}(new 
     * {@link HTML#HTML(String) HTML}(htmlFragment))</code><br/>
     * 
     * @see HTML
     * @param htmlFragment
     *            any valid HTML structure
     */
    public void add(String htmlFragment) {
        add(new HTML(htmlFragment));
    }

    /* (non-Javadoc)
     * @see com.google.gwt.user.client.ui.HasWidgets#clear()
     */
    @Override
    public void clear() {
        Iterator<com.google.gwt.user.client.ui.Widget> it = iterator();
        while (it.hasNext()) {
          it.next();
          it.remove();
        }
    }

    /**
     * Removes a child widget.
     * 
     * <p>
     * <b>How to Override this Method</b>
     * </p>
     * <p>
     * There are several important things that must take place in the correct
     * order to properly remove a Widget from a Panel. Not all of these steps
     * will be relevant to every Panel, but all of the steps must be considered.
     * <ol>
     * <li><b>Validate:</b> Make sure this Panel is actually the parent of the
     * child Widget; return <code>false</code> if it is not.</li>
     * <li><b>Orphan:</b> Call {@link #orphan(Widget)} first while the child
     * Widget is still attached.</li>
     * <li><b>Physical Detach:</b> Adjust the DOM to account for the removal of
     * the child Widget. The Widget's Element must be physically removed from
     * the DOM.</li>
     * <li><b>Logical Detach:</b> Update the Panel's state variables to reflect
     * the removal of the child Widget. Example: the Widget is removed from the
     * Panel's {@link WidgetCollection}.</li>
     * </ol>
     * </p>
     * 
     * @param child
     *            the widget to be removed
     * @return <code>true</code> if the child was present
     */
    public abstract boolean remove(com.google.gwt.user.client.ui.Widget child);

    /* (non-Javadoc)
     * @see com.google.gwt.user.client.ui.Widget#doAttachChildren()
     */
    @Override
    protected void doAttachChildren() {
        // Ensure that all child widgets are attached.
        for (Iterator<com.google.gwt.user.client.ui.Widget> it = iterator(); it.hasNext();) {
            com.google.gwt.user.client.ui.Widget child = it.next();
            childOnAttach(child);
        }
    }

    /* (non-Javadoc)
     * @see com.google.gwt.user.client.ui.Widget#doDetachChildren()
     */
    @Override
    protected void doDetachChildren() {
        // Ensure that all child widgets are detached.
        for (Iterator<com.google.gwt.user.client.ui.Widget> it = iterator(); it.hasNext();) {
            com.google.gwt.user.client.ui.Widget child = it.next();
            childOnDetach(child);
        }
    }
    
    /**
     * Gets the child Dijit Widgets physically attached to this Widgets.
     * @return the child widgets found
     */
    public List<Widget> getDijitChildren(){
        return Dijit.byNode(Dojo.query("> ["+WidgetEngine.WIDGET_ID+"]", getContainerElement()));
    }
    
    /**
     * Gets all Dijit Widgets physically attached to this Widgets.
     * @return all widgets found, this children, and the children of the children
     */
    public List<Widget> getDijitDescendants(){
        return Dijit.byNode(Dojo.query("["+WidgetEngine.WIDGET_ID+"]", getContainerElement()));
    }

}
